/*
 * Copyright (c) 2023 Institute of Parallel And Distributed Systems (IPADS), Shanghai Jiao Tong University (SJTU)
 * Licensed under the Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *     http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
 * PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
#ifndef IPC_CONNECTION_H
#define IPC_CONNECTION_H

#include <object/thread.h>

/*
 * An ipc_server_handler_thread has such config which stores its information.
 * Note that the ipc_server_handler_thread is used for handling IPC requests.
 */
struct ipc_server_handler_config {
    /* Avoid invoking the same handler_thread concurrently */
    struct lock ipc_lock;

    /* PC */
    vaddr_t ipc_routine_entry;
    /* SP */
    vaddr_t ipc_routine_stack;
    /* Entry point of shadow thread exit routine */
    vaddr_t ipc_exit_routine_entry;
    vaddr_t destructor;

    /*
     * Record which connection uses this handler thread now.
     * Multiple connection can use the same handler_thread.
     */
    struct ipc_connection *active_conn;
};

/*
 * An ipc_server_register_cb_thread has such config which stores its
 * information. This thread is used for handling IPC registration.
 */
struct ipc_server_register_cb_config {
    struct lock register_lock;
    /* PC */
    vaddr_t register_cb_entry;
    /* SP */
    vaddr_t register_cb_stack;
    vaddr_t destructor;

    /* The caps for the connection currently building */
    cap_t conn_cap_in_client;
    /* Not used now (can be exposed to server in future) */
    cap_t conn_cap_in_server;
    cap_t shm_cap_in_server;
};

/*
 * An ipc_server_thread which invokes "reigster_server" has such config.
 * This thread, which declares an IPC service in the server process,
 * will be exposed to clients. Then, clients invokes "register_client"
 * with such ipc_server_thread.
 */
struct ipc_server_config {
    /* Callback_thread for handling client registration */
    struct thread *register_cb_thread;

    /* Record the argument from the server thread */
    unsigned long declared_ipc_routine_entry;
};

/*
 * Each connection owns one shm for exchanging data between client and server.
 * Client process registers one PMO_SHM and copies the shm_cap to the server.
 * But, client and server can map the PMO_SHM at different addresses.
 */
struct shm_for_ipc_connection {
    /*
     * The starting address of the shm in the client process's vmspace.
     * uaddr: user-level virtual address.
     */
    vaddr_t client_shm_uaddr;

    /* The starting address of the shm in the server process's vmspace. */
    vaddr_t server_shm_uaddr;
    size_t shm_size;

    /* For resource recycle */
    cap_t shm_cap_in_client;
    cap_t shm_cap_in_server;
};

struct ipc_connection {
    /*
     * current client who uses this connection.
     * Note that all threads in the client process can use this connection.
     */
    struct thread *current_client_thread;

    /*
     * server_handler_thread is always fixed after establishing the
     * connection.
     * i.e., ipc_server_handler_thread
     */
    struct thread *server_handler_thread;

    /*
     * Identification of the client (cap_group).
     * This badge is always fixed with the ipc_connection and
     * will be transferred to the server during each IPC.
     * Thus, the server can identify different client processes.
     *
     * NOTE: an connection cannot be shared between multiple clients.
     */
    badge_t client_badge;

    /* The vaddr of user_ipc_msg: used when returning caps from server to client */
    struct ipc_msg *user_ipc_msg;

    struct shm_for_ipc_connection shm;

    /* For resource recycle */
    struct lock ownership;
    cap_t conn_cap_in_client;
    cap_t conn_cap_in_server;
    int is_valid;
};

struct ipc_msg {
    unsigned int data_len;
    unsigned int cap_slot_number;
    unsigned int data_offset;
    unsigned int cap_slots_offset;
};

void connection_deinit(void *conn);

/* IPC related system calls */
int sys_register_server(unsigned long ipc_rountine, cap_t register_cb_cap,
                        unsigned long destructor);
cap_t sys_register_client(cap_t server_cap, unsigned long vm_config_ptr);
int sys_ipc_register_cb_return(cap_t server_thread_cap,
                               unsigned long server_thread_exit_routine,
                               unsigned long server_shm_addr);

unsigned long sys_ipc_call(cap_t conn_cap, struct ipc_msg *ipc_msg,
                           unsigned int cap_num);
int sys_ipc_return(unsigned long ret, unsigned int cap_num);
void sys_ipc_exit_routine_return(void);

#endif /* IPC_CONNECTION_H */